package com.hqd.ch03.v39.web.servlet.view;

import com.hqd.ch03.v39.web.servlet.View;
import com.hqd.ch03.v39.web.servlet.ViewResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class AbstractCachingViewResolver implements ViewResolver {
    /**
     * 缓存个数
     */
    public static final int DEFAULT_CACHE_LIMIT = 1024;

    /**
     * 为找到视图
     */
    private static final View UNRESOLVED_VIEW = new View() {
        @Override

        public String getContentType() {
            return null;
        }

        @Override
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) {
        }
    };

    /**
     * 过滤器
     */
    private static final CacheFilter DEFAULT_CACHE_FILTER = (view, viewName, locale) -> true;
    private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<>(DEFAULT_CACHE_LIMIT);
    private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;
    @SuppressWarnings("serial")
    private final Map<Object, View> viewCreationCache =
            new LinkedHashMap<Object, View>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
                /**
                 *清楚策略，超过定义的大小，则清除最早插入的元素
                 */
                @Override
                protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) {
                    if (size() > getCacheLimit()) {
                        viewAccessCache.remove(eldest.getKey());
                        return true;
                    } else {
                        return false;
                    }
                }
            };
    private boolean cacheUnresolved = true;
    private CacheFilter cacheFilter = DEFAULT_CACHE_FILTER;


    public int getCacheLimit() {
        return this.cacheLimit;
    }

    public void setCacheLimit(int cacheLimit) {
        this.cacheLimit = cacheLimit;
    }

    public boolean isCache() {
        return (this.cacheLimit > 0);
    }

    public void setCache(boolean cache) {
        this.cacheLimit = (cache ? DEFAULT_CACHE_LIMIT : 0);
    }

    public boolean isCacheUnresolved() {
        return this.cacheUnresolved;
    }

    public void setCacheUnresolved(boolean cacheUnresolved) {
        this.cacheUnresolved = cacheUnresolved;
    }

    public CacheFilter getCacheFilter() {
        return this.cacheFilter;
    }

    public void setCacheFilter(CacheFilter cacheFilter) {
        this.cacheFilter = cacheFilter;
    }

    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        /**
         * 没有开启缓存
         */
        if (!isCache()) {
            return createView(viewName, locale);
        } else {
            Object cacheKey = getCacheKey(viewName, locale);
            /**
             * 缓存中获取
             */
            View view = this.viewAccessCache.get(cacheKey);
            if (view == null) {
                synchronized (this.viewCreationCache) {
                    view = this.viewCreationCache.get(cacheKey);
                    if (view == null) {
                        // 不存在则创建视图
                        view = createView(viewName, locale);
                        if (view == null && this.cacheUnresolved) {
                            view = UNRESOLVED_VIEW;
                        }
                        if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
                            this.viewAccessCache.put(cacheKey, view);
                            this.viewCreationCache.put(cacheKey, view);
                        }
                    }
                }
            }
            //没有找到对应视图，返回空
            return (view != UNRESOLVED_VIEW ? view : null);
        }
    }

    protected Object getCacheKey(String viewName, Locale locale) {
        return viewName + '_' + locale;
    }

    public void removeFromCache(String viewName, Locale locale) {
        Object cacheKey = getCacheKey(viewName, locale);
        Object cachedView;
        synchronized (this.viewCreationCache) {
            this.viewAccessCache.remove(cacheKey);
            cachedView = this.viewCreationCache.remove(cacheKey);
        }
    }

    public void clearCache() {
        synchronized (this.viewCreationCache) {
            this.viewAccessCache.clear();
            this.viewCreationCache.clear();
        }
    }


    protected View createView(String viewName, Locale locale) throws Exception {
        return loadView(viewName, locale);
    }

    protected abstract View loadView(String viewName, Locale locale) throws Exception;


    @FunctionalInterface
    public interface CacheFilter {

        boolean filter(View view, String viewName, Locale locale);
    }
}
